package projectx.core.dao.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

import projectx.core.dao.Parameter;
import projectx.db.DBQuote;

/**
 * <dl>
 * <dt><b>クラス概要： </b></dt>
 * <dd>PreparedStatement用のSQLビルダー・クラスです。 <br>
 * このクラスは次の構文規則に基づいてSQLテンプレート文字列を解析し、 SQLテンプレートの構文要素リストを内部で保持します。 <br>
 * &lt;SQLTemplate&gt; ::= (&lt;Variable&gt; | &lt;WhereAnd&gt; |
 * &lt;WhereOr&gt; | &lt;Constant&gt;)* <br>
 * &lt;Variable&gt; ::= :[_a-zA-Z0-9]* <br>
 * &lt;WhereAnd&gt; ::= :_WHERE_AND(&lt;Condition&gt;* (, &lt;Condition&gt;)*)
 * <br>
 * &lt;WhereOr&gt; ::= :_WHERE_OR(&lt;Condition&gt;* (, &lt;Condition&gt;)*)
 * <br>
 * &lt;Condition&gt; ::= &lt;VariableCondition&gt; | &lt;ConstantCondition&gt;
 * <br>
 * &lt;VariableCondition&gt; ::= &lt;IsNullableCondition&gt; |
 * &lt;IsNotNullableCondition&gt; | &lt;OtherCondition&gt; <br>
 * &lt;IsNullableCondition&gt; ::= &lt;Constant&gt;* = &lt;Variable&gt;
 * &lt;Constant&gt;* <br>
 * &lt;IsNotNullableCondition&gt; ::= &lt;Constant&gt;* (!= | &lt;&gt; | ^=)
 * &lt;Variable&gt; &lt;Constant&gt;* <br>
 * &lt;OtherCondition&gt; ::= &lt;Constant&gt;* &lt;Variable&gt;
 * &lt;Constant&gt;* <br>
 * &lt;ConstantCondition&gt; ::= &lt;Constant&gt;* <br>
 * &lt;Constant&gt; ::= &lt;Variable&gt;以外の文字列 <br>
 * *1." "(スペース)は無視されます。 <br>
 * *2.":"(コロン)は変数の開始を表す特別な文字です。 <br>
 * *3."'"(シングル・クォーテーション)、 """(ダブル・クォーテーション)で囲まれた範囲では、 " "および":"が通常の文字として認識されます。
 * <br>
 * *4."'"で囲まれた範囲内に"'"を含めたい場合は、 "'"を２つ続けて記述("''")してください。 <br>
 * <br>
 * 各要素の意味は次のとおりです。 <br>
 * <table><thead>
 * <tr>
 * <td>要素</td>
 * <td>概要</td>
 * <td>例</td>
 * </tr>
 * </thead> <tbody>
 * <tr>
 * <td>SQLTemplate</td>
 * <td>SQLテンプレート全体を表す要素です。</td>
 * <td>UPDATE EMPLOYEE SET LASTNAME = :NEWNAME :_WHERE_AND(EMPNO = :EMPNO,
 * FIRSTNME &lt;&gt; :FIRSTNME, LASTNAME LIKE :LASTNAME, SEX = 'M')</td>
 * </tr>
 * <tr>
 * <td>Variable</td>
 * <td>変数要素です。SQL発行時に指定されたパラメーターの値によって置換されます。</td>
 * <td>:NEWNAME, :EMPNO, :FIRSTNME, :LASTNAME</td>
 * </tr>
 * <tr>
 * <td>WhereAnd</td>
 * <td>WhereAnd要素です。複数の条件から構成されます。SQL発行時に指定されたパラメーターの値に応じて、 条件を"AND"で連結します。
 * </td>
 * <td>:_WHERE_AND(EMPNO = :EMPNO, FIRSTNME &lt;&gt; :FIRSTNME, LASTNAME LIKE
 * :LASTNAME, SEX = 'M')</td>
 * </tr>
 * <tr>
 * <td>WhereOr</td>
 * <td>WhereOr要素です。複数の条件から構成されます。SQL発行時に指定されたパラメーターの値に応じて、 条件を"OR"で連結します。</td>
 * <td>:_WHERE_OR(EMPNO = :EMPNO, FIRSTNME &lt;&gt; :FIRSTNME, LASTNAME LIKE
 * :LASTNAME, SEX = 'M')</td>
 * </tr>
 * <tr>
 * <td>Condition</td>
 * <td>条件要素です。可変/不変条件要素の抽象要素です。</td>
 * <td>EMPNO = :EMPNO, FIRSTNME &lt;&gt; :FIRSTNME, LASTNAME LIKE :LASTNAME,
 * SEX = 'M'</td>
 * </tr>
 * <tr>
 * <td>VariableCondition</td>
 * <td>可変条件要素です。IS NULL/IS NOT NULL/通常条件要素の抽象要素です。</td>
 * <td>EMPNO = :EMPNO, FIRSTNME &lt;&gt; :FIRSTNME, LASTNAME LIKE :LASTNAME
 * </td>
 * </tr>
 * <tr>
 * <td>IsNullableCondition</td>
 * <td>IS NULL条件要素です。パラメーターの値がnullである場合は、"="を"IS NULL"に置換します。</td>
 * <td>EMPNO = :EMPNO</td>
 * </tr>
 * <tr>
 * <td>IsNotNullableCondition</td>
 * <td>IS NOT NULL条件要素です。 パラメーターの値がnullである場合は、"!="/"&lt;&gt;"/"^="を"IS NOT
 * NULL"に置換します。</td>
 * <td>FIRSTNME &lt;&gt; :FIRSTNME</td>
 * </tr>
 * <tr>
 * <td>OtherCondition</td>
 * <td>通常条件要素です。IsNullable/IsNotNullableのどちらにも該当しない可変条件要素です。</td>
 * <td>LASTNAME LIKE :LASTNAME</td>
 * </tr>
 * <tr>
 * <td>ConstantCondition</td>
 * <td>不変条件要素です。パラメーターの値によらず、常に適用されます。</td>
 * <td>SEX = 'M'</td>
 * </tr>
 * <tr>
 * <td>Constant</td>
 * <td>定数要素です。変数/WhereAnd/WhereOr要素に該当しない文字列です。</td>
 * <td>UPDATE,EMPLOYEE,SET,LASTNAME,=,,,</td>
 * </tr>
 * </tbody> </table> <br>
 * 変換例 <br>
 * <ul>
 * <li>SQLテンプレート:UPDATE EMPLOYEE SET LASTNAME = :NEWNAME :_WHERE_AND(EMPNO =
 * :EMPNO, FIRSTNME = :FIRSTNME)</li>
 * <li>パラメーター名:NEWNAME,EMPNO</li>
 * <li>パラメーター値:O'CONNELL,000010</li>
 * <li>変換後のSQL:UPDATE EMPLOYEE SET LASTNAME = ? WHERE EMPNO = ?</li>
 * </ul>
 * このファイルはDAO Generator V1.3.2によって自動生成されました。</dd>
 * </dl>
 */
public class PreparedStatementBuilder {

	/**
	 * SQLテンプレート上に変数が存在し、かつ、 パラメーターが存在しない場合の例外メッセージです。
	 */
	public static final String MSG_PARAMETER_NOT_FOUND = "パラメーターがセットされていません。({0})";

	/**
	 * WhereAnd/WhereOr要素内に複数の変数が定義されていた場合の例外メッセージです。
	 */
	public static final String MSG_PLURAL_VARIABLE = "一つの条件内に複数の変数を定義することはできません。";

	/**
	 * WhereAnd/WhereOr要素の終端記号が存在しない場合の例外メッセージです。
	 */
	public static final String MSG_UNCLOSED_PARENTHESIS = "条件の終端記号\")\"が存在しません。";

	/**
	 * " "(スペース)です
	 */
	private static final String SPACE = " ";

	/**
	 * SQLテンプレート構文要素のリストです
	 */
	private List _sqlElements = null;

	/**
	 * SQLテンプレートです
	 */
	private String _sqlTemplate = null;

	/**
	 * 不変SQL(WhereAnd/WhereOr/Variableを含まない)フラグです。
	 */
	private boolean _invariable = true;

	/**
	 * 不変SQL(WhereAnd/WhereOr/Variableを含まない)です。
	 */
	private String _invariableSql = null;

	/**
	 * SQLのPara List
	 */
	private List _sqlParaList = new ArrayList();

	/**
	 * <dl>
	 * <dt><b>コンストラクター概要： </b></dt>
	 * <dd>コンストラクターです。 <br>
	 * SQLテンプレートの文字列を、 SQL要素クラスのリストに解析します。</dd>
	 * </dl>
	 * 
	 * @param sqlTemplate
	 *            SQLテンプレート
	 * @exception IllegalArgumentException
	 *                解析エラーが発生した場合
	 */
	public PreparedStatementBuilder(String sqlTemplate)
			throws IllegalArgumentException {

		_sqlTemplate = sqlTemplate;
		_sqlElements = new Parser().parse();
		//Debug log ため
		if (_sqlParaList.size() > 0){
			_sqlParaList.clear();
		}
		_sqlParaList.add(0, sqlTemplate);

		// SQLを事前に組み立てる
		// ただし、WhereAnd/WhereOr要素が存在する場合は組み立てを中止する
		StringBuffer sql = new StringBuffer();

		int elementsSize = _sqlElements.size();

		for (int i = 0; i < elementsSize ; i++) {
			I_Element e = (I_Element) _sqlElements.get(i);
			if ((e instanceof WhereAnd) || (e instanceof WhereOr)
					|| (e instanceof Variable)) {
				_invariable = false;
				return;
			} else {
				sql.append(e.format(null, null));
			}
			if (i != elementsSize - 1) {
				sql.append(SPACE);
			}
		}
		_invariableSql = sql.toString();
	}

	/**
	 * <dl>
	 * <dt><b>メソッド概要： </b></dt>
	 * <dd>SQLテンプレートを取得します。</dd>
	 * </dl>
	 * 
	 * @return SQLテンプレート
	 */
	public String getSqlTemplate() {
		return _sqlTemplate;
	}

	/**
	 * <dl>
	 * <dt><b>メソッド概要： </b></dt>
	 * <dd>SQL要素クラスのリストから、 照会処理用のSQLを組み立てます。 <br>
	 * また、パラメーター名を引数のリストへ順番に追加します。 <br>
	 * 不変SQL(WhereAnd/WhereOrを含まない)である場合は、 param, paramNamesともにnullで構いません。 <br>
	 * </dd>
	 * </dl>
	 * 
	 * @param param
	 *            パラメーター
	 * @param paramNames
	 *            パラメーター名のリスト
	 * @return SQL
	 * @exception IllegalArgumentException
	 *                パラメーター値が不足している場合
	 */
	public String getSql(Parameter param, List paramNames)
			throws IllegalArgumentException {

		// WhereAnd/WhereOrを含まない場合は不変SQLを返す
		if (_invariable) {
			if (_sqlParaList.size() > 0){
				_sqlParaList.clear();
			}
			_sqlParaList.add(0, _invariableSql);
			return _invariableSql;
		}
		StringBuffer sql = new StringBuffer(_sqlTemplate.length());
		
		int elementSize = _sqlElements.size();
		
		for (int i = 0; i < elementSize; i++) {
			sql
					.append(((I_Element) _sqlElements.get(i)).format(param,
							paramNames));
			if (i != elementSize - 1) {
				sql.append(SPACE);
			}
		}
		if (_sqlParaList.size() > 0){
			_sqlParaList.clear();
		}
		_sqlParaList.add(0, sql.toString());

		return sql.toString();
	}

	/**
	 * <dl>
	 * <dt><b>メソッド概要： </b></dt>
	 * <dd>
	 * 
	 * ログためのSQLを取得する <br>
	 * </dd>
	 * </dl>
	 * by 蒋振杉
	 * @return sqlLog
	 */
	public String getSqlLog() {

		if (_sqlParaList == null || _sqlParaList.size() == 0) {
			return "SQL is null";
		}
		String sql = _sqlParaList.get(0).toString();
		StringBuffer newsql = new StringBuffer();
		try {
			if ("".equals(sql)) {
				return "SQL is null";
			}

			int cnt = sql.length();
			int idx = 1;
			for (int i = 0; i < cnt; i++) {
				char c = sql.charAt(i);
				if (c == '?') {
					// if (idx <= sqlParaList.size()) {
					if (idx < _sqlParaList.size()) {
						newsql.append(_sqlParaList.get(idx).toString());
						idx++;
					} else {
						newsql.append("Empty!!!");
					}
				} else {
					newsql.append(c);
				}
			}
		} catch (Exception e) {
			return "";
		}
		return newsql.toString();
	}

	/**
	 * sqlをセット
	 * @param sql sql
	 */
	public void setSql(String sql) {
		if(_sqlParaList == null){
			return;
		}

		_sqlParaList.remove(0);
		_sqlParaList.add(0, sql);
	}

	private String toValue(Object value, int valueType) {
		if (value == null) {
			return "NULL";
		}
		switch (valueType) {
		case Types.VARCHAR:
			return DBQuote.quoteChar(value.toString());
		case Types.TINYINT:
			return value.toString();
		case Types.SMALLINT:
			return value.toString();
		case Types.INTEGER:
			return value.toString();
		case Types.BIGINT:
			return value.toString();
		case Types.REAL:
			return value.toString();
		case Types.DOUBLE:
			return value.toString();
		case Types.DATE:
			return DBQuote.quoteChar(value.toString());
		case Types.TIME:
			return DBQuote.quoteChar(value.toString());
		case Types.TIMESTAMP:
			return DBQuote.quoteChar(value.toString());
		case Types.BLOB:
			return " 'BLOB' ";
		case Types.CLOB:
			return " 'CLOB' ";
		default:
			return value.toString();
		}
	}

	/**
	 * <dl>
	 * <dt><b>メソッド概要： </b></dt>
	 * <dd>スクロール可否/更新可否を判定し、PreparedStatementインスタンスを生成します。</dd>
	 * </dl>
	 * 
	 * @param con
	 *            Connectionインスタンス
	 * @param sql
	 *            SQL
	 * @param scrollable
	 *            スクロール可否
	 * @param updatable
	 *            更新可否
	 * @return PreparedStatementインスタンス
	 * @exception SQLException
	 *                SQLエラーが発生した場合
	 */
	public PreparedStatement createStatement(Connection con, String sql,
			boolean scrollable, boolean updatable) throws SQLException {
		int typeScroll = scrollable ? ResultSet.TYPE_SCROLL_INSENSITIVE
				: ResultSet.TYPE_FORWARD_ONLY;
		int typeUpdatable = updatable ? ResultSet.CONCUR_UPDATABLE
				: ResultSet.CONCUR_READ_ONLY;
		return con.prepareStatement(sql, typeScroll, typeUpdatable);
	}

	/**
	 * <dl>
	 * <dt><b>メソッド概要： </b></dt>
	 * <dd>PreparedStatementにパラメーター値を指定します。 <br>
	 * パラメーター値がnullである場合は、 PreparedStatement#setNull(int, int)を使用します。 <br>
	 * <br>
	 * それ以外のデータ型である場合は、 各データ型に応じたメソッドによりパラメーター値を指定します。</dd>
	 * </dl>
	 * 
	 * @param ps
	 *            PreparedStatementインスタンス
	 * @param index
	 *            プレースメント・インデックス
	 * @param param
	 *            パラメーター
	 * @param name
	 *            パラメーター名
	 * @exception SQLException
	 *                SQLエラーが発生した場合
	 * @exception IOException
	 *                ストリーム使用中に入出力例外が発生した場合
	 * @see java.sql.Types
	 */
	public void setParameter(PreparedStatement ps, int index, Parameter param,
			String name) throws SQLException, IOException {
		Object value = param.getObject(name);
		int sqlType = param.getSqlType(name);
		//Logため追加する
		_sqlParaList.add(toValue(value, sqlType));

		if (value == null) {
			ps.setNull(index, sqlType);
			return;
		}

		switch (sqlType) {
		case Types.VARCHAR:
			ps.setString(index, value.toString());
			break;
		case Types.TINYINT:
			ps.setByte(index, ((Byte) value).byteValue());
			break;
		case Types.SMALLINT:
			ps.setShort(index, ((Short) value).shortValue());
			break;
		case Types.INTEGER:
			ps.setInt(index, ((Integer) value).intValue());
			break;
		case Types.BIGINT:
			ps.setLong(index, ((Long) value).longValue());
			break;
		case Types.REAL:
			ps.setFloat(index, ((Float) value).floatValue());
			break;
		case Types.DOUBLE:
			ps.setDouble(index, ((Double) value).doubleValue());
			break;
		case Types.DATE:
			ps.setDate(index, (Date) value);
			break;
		case Types.TIME:
			ps.setTime(index, (Time) value);
			break;
		case Types.TIMESTAMP:
			ps.setTimestamp(index, (Timestamp) value);
			break;
		case Types.DECIMAL:
			ps.setBigDecimal(index, (BigDecimal) value);
			break;
		case Types.BLOB:
			ps.setBinaryStream(index, ((InputStream) value),
					((InputStream) value).available());
			break;
		case Types.CLOB:
			ps.setCharacterStream(index, ((Reader) value), param.length(name));
			break;
		default:
			ps.setObject(index, value);
			break;
		}
	}

	/**
	 * <dl>
	 * <dt><b>メソッド概要： </b></dt>
	 * <dd>PreparedStatementにパラメーター値を指定します。 <br>
	 * パラメーター値がnullである場合は、 PreparedStatement#setNull(int, int)を使用します。 <br>
	 * <br>
	 * それ以外のデータ型である場合は、 各データ型に応じたメソッドによりパラメーター値を指定します。</dd>
	 * </dl>
	 * 
	 * @param ps
	 *            PreparedStatementインスタンス
	 * @param param
	 *            パラメーター
	 * @param paramNames
	 *            パラメーター名のリスト
	 * @exception SQLException
	 *                SQLエラーが発生した場合
	 * @exception IOException
	 *                ストリーム使用中に入出力例外が発生した場合
	 * @see java.sql.Types
	 */
	public void setParameter(PreparedStatement ps, Parameter param,
			List paramNames) throws SQLException, IOException {
		
		int paramSize = paramNames.size();
		
		for (int i = 0; i < paramSize; i++) {
			String name = (String) paramNames.get(i);
			Object value = param.getObject(name);
			int sqlType = param.getSqlType(name);
			
			_sqlParaList.add(toValue(value, sqlType));
			if (value == null) {
				ps.setNull(i + 1, sqlType);
				continue;
			}

			switch (sqlType) {
			case Types.VARCHAR:
				ps.setString(i + 1, value.toString());
				break;
			case Types.TINYINT:
				ps.setByte(i + 1, ((Byte) value).byteValue());
				break;
			case Types.SMALLINT:
				ps.setShort(i + 1, ((Short) value).shortValue());
				break;
			case Types.INTEGER:
				ps.setInt(i + 1, ((Integer) value).intValue());
				break;
			case Types.BIGINT:
				ps.setLong(i + 1, ((Long) value).longValue());
				break;
			case Types.REAL:
				ps.setFloat(i + 1, ((Float) value).floatValue());
				break;
			case Types.DOUBLE:
				ps.setDouble(i + 1, ((Double) value).doubleValue());
				break;
			case Types.DATE:
				ps.setDate(i + 1, (Date) value);
				break;
			case Types.TIME:
				ps.setTime(i + 1, (Time) value);
				break;
			case Types.TIMESTAMP:
				ps.setTimestamp(i + 1, (Timestamp) value);
				break;
			case Types.DECIMAL:
				ps.setBigDecimal(i + 1, (BigDecimal) value);
				break;
			case Types.BLOB:
				ps.setBinaryStream(i + 1, ((InputStream) value),
						((InputStream) value).available());
				break;
			case Types.CLOB:
				ps.setCharacterStream(i + 1, ((Reader) value), param
						.length(name));
				break;
			default:
				ps.setObject(i + 1, value);
				break;
			}
		}
	}

	/**
	 * <dl>
	 * <dt><b>メソッド概要： </b></dt>
	 * <dd>PreparedStatementにパラメーター値を指定します。 <br>
	 * パラメーター値がnullである場合は、 PreparedStatement#setNull(int, int)を使用します。 <br>
	 * <br>
	 * それ以外のデータ型である場合は、 各データ型に応じたメソッドによりパラメーター値を指定します。</dd>
	 * </dl>
	 * 
	 * @param ps
	 *            PreparedStatementインスタンス
	 * @param index
	 *            プレースメント・インデックス
	 * @param value
	 *            パラメーター値
	 * @param sqlType
	 *            JDBCデータ型
	 * @exception SQLException
	 *                SQLエラーが発生した場合
	 * @exception IOException
	 *                ストリーム使用中に入出力例外が発生した場合
	 * @see java.sql.Types
	 */
	public void setParameter(PreparedStatement ps, int index, Object value,
			int sqlType) throws SQLException, IOException {

		//Logため追加する by Jiangzs
		_sqlParaList.add(toValue(value, sqlType));

		if (value == null) {
			ps.setNull(index, sqlType);
			return;
		}

		switch (sqlType) {
		case Types.VARCHAR:
			ps.setString(index, value.toString());
			break;
		case Types.TINYINT:
			ps.setByte(index, ((Byte) value).byteValue());
			break;
		case Types.SMALLINT:
			ps.setShort(index, ((Short) value).shortValue());
			break;
		case Types.INTEGER:
			ps.setInt(index, ((Integer) value).intValue());
			break;
		case Types.BIGINT:
			ps.setLong(index, ((Long) value).longValue());
			break;
		case Types.REAL:
			ps.setFloat(index, ((Float) value).floatValue());
			break;
		case Types.DOUBLE:
			ps.setDouble(index, ((Double) value).doubleValue());
			break;
		case Types.DATE:
			ps.setDate(index, (Date) value);
			break;
		case Types.TIME:
			ps.setTime(index, (Time) value);
			break;
		case Types.TIMESTAMP:
			ps.setTimestamp(index, (Timestamp) value);
			break;
		case Types.DECIMAL:
			ps.setBigDecimal(index, (BigDecimal) value);
			break;
		case Types.BLOB:
			ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) value);
			ps.setBinaryStream(index, bais, bais.available());
			break;
		case Types.CLOB:
			StringReader sr = new StringReader((String) value);
			ps.setCharacterStream(index, sr, ((String) value).length());
			break;
		default:
			ps.setObject(index, value);
			break;
		}
	}

	/**
	 * <dl>
	 * <dt><b>クラス概要： </b></dt>
	 * <dd>テンプレート構文を構成する要素インターフェースです。</dd>
	 * </dl>
	 */
	private static interface I_Element {

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>構文要素文字列を取得します。</dd>
		 * </dl>
		 * 
		 * @param param
		 *            パラメーター
		 * @param paramNames
		 *            パラメーター名のリスト
		 * @return 要素文字列
		 * @exception IllegalArgumentException
		 *                要素文字列取得中にエラーが発生した場合
		 */
		String format(Parameter param, List paramNames)
				throws IllegalArgumentException;
	}

	/**
	 * <dl>
	 * <dt><b>クラス概要： </b></dt>
	 * <dd>SQLテンプレートの変数(ex. :FOO)要素クラスです。</dd>
	 * </dl>
	 */
	private static class Variable implements I_Element {

		/**
		 * 変数名です。
		 */
		private String _name = null;

		/**
		 * <dl>
		 * <dt><b>コンストラクター概要： </b></dt>
		 * <dd>コンストラクターです。</dd>
		 * </dl>
		 * 
		 * @param name
		 *            変数名
		 */
		private Variable(String name) {
			_name = name;
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>常に"?"を返します。 このとき、パラメーター名をパラメーター名のリストに追加します。</dd>
		 * </dl>
		 * 
		 * @param param
		 *            パラメーター
		 * @param paramNames
		 *            パラメーター名のリスト
		 * @return パラメーター値の文字列
		 * @exception IllegalArgumentException
		 *                パラメーターが不足していた場合
		 */
		public String format(Parameter param, List paramNames) {
			if (param != null) {
				String replacement = param.getReplacement(_name);
				if (replacement != null) {
					return replacement;
				}
				if (param.containsKey(_name)) {
					paramNames.add(_name);
					return "?";
				}
			}
			return "?";
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>変数名を取得します。</dd>
		 * </dl>
		 * 
		 * @return 変数名
		 */
		private String getName() {
			return _name;
		}
	}

	/**
	 * <dl>
	 * <dt><b>クラス概要： </b></dt>
	 * <dd>SQLテンプレートの固定文字列(ex. "SELECT", 'FOO')要素クラスです。</dd>
	 * </dl>
	 */
	private static class Constant implements I_Element {

		/**
		 * 固定文字列です。
		 */
		private String _constant = null;

		/**
		 * コンストラクターです。
		 * 
		 * @param constant
		 *            固定文字列
		 */
		private Constant(String constant) {
			_constant = constant;
		}

		/**
		 * 固定文字列を返します。
		 * 
		 * @param param
		 *            パラメーター。このメソッドでは使用しません。
		 * @param paramNames
		 *            パラメーター名のリスト。このメソッドでは使用しません。
		 * @return 固定文字列
		 */
		public String format(Parameter param, List paramNames) {
			return _constant;
		}
	}

	/**
	 * <dl>
	 * <dt><b>クラス概要： </b></dt>
	 * <dd>SQLテンプレートのWhereAnd要素クラスです。 指定されたパラメーターの数に応じて条件をANDで連結します。</dd>
	 * </dl>
	 */
	private static class WhereAnd implements I_Element {

		/**
		 * 条件のリストです。
		 */
		private List _conditions = null;

		/**
		 * <dl>
		 * <dt><b>コンストラクター概要： </b></dt>
		 * <dd>コンストラクターです。</dd>
		 * </dl>
		 * 
		 * @param conditions
		 *            条件のリスト
		 */
		private WhereAnd(List conditions) {
			_conditions = conditions;
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>指定されたパラメーター値の数に応じて、 WHERE句の条件をANDにより連結します。 <br>
		 * </dd>
		 * </dl>
		 * 
		 * @param param
		 *            パラメーター
		 * @param paramNames
		 *            パラメーター名のリスト。このメソッドでは使用しません。
		 * @return ANDによって結合されたWHERE条件
		 * @exception IllegalArgumentException
		 *                パラメーター値が不足していた場合
		 */
		public String format(Parameter param, List paramNames)
				throws IllegalArgumentException {

			//有効な条件のみを抽出
			List validConditions = new ArrayList();
			for (int i = 0; i < _conditions.size(); i++) {
				String conditionStr = ((Condition) _conditions.get(i)).format(
						param, paramNames);
				if (!"".equals(conditionStr)) {
					validConditions.add(conditionStr);
				}
			}
			//条件が存在しない場合は空文字列を返す
			if (validConditions.isEmpty()) {
				return "";
			}

			StringBuffer sb = new StringBuffer();
			sb.append("WHERE");
			sb.append(SPACE);
			for (int i = 0; i < validConditions.size(); i++) {
				sb.append(validConditions.get(i));
				if (i < validConditions.size() - 1) {
					sb.append(SPACE);
					sb.append("AND");
					sb.append(SPACE);
				}
			}
			return sb.toString();
		}
	}

	/**
	 * <dl>
	 * <dt><b>クラス概要： </b></dt>
	 * <dd>SQLテンプレートのWhereOr要素クラスです。 指定されたパラメーターの数に応じて条件をORで連結します。</dd>
	 * </dl>
	 */
	private static class WhereOr implements I_Element {

		/**
		 * 条件のリストです
		 */
		private List _conditions = null;

		/**
		 * <dl>
		 * <dt><b>コンストラクター概要： </b></dt>
		 * <dd>コンストラクターです。</dd>
		 * </dl>
		 * 
		 * @param conditions
		 *            条件のリスト
		 */
		private WhereOr(List conditions) {
			_conditions = conditions;
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>指定されたパラメーター値の数に応じて、 WHERE句の条件をORにより連結します。 <br>
		 * </dd>
		 * </dl>
		 * 
		 * @param param
		 *            パラメーター
		 * @param paramNames
		 *            パラメーター名のリスト。このメソッドでは使用しません。
		 * @return ORによって結合されたWHERE条件
		 * @exception IllegalArgumentException
		 *                パラメーター値が不足していた場合
		 */
		public String format(Parameter param, List paramNames) {
			//有効な条件のみを抽出
			List validConditions = new ArrayList();
			for (int i = 0; i < _conditions.size(); i++) {
				String conditionStr = ((Condition) _conditions.get(i)).format(
						param, paramNames);
				if (!"".equals(conditionStr)) {
					validConditions.add(conditionStr);
				}
			}
			//条件が存在しない場合は空文字列を返す
			if (validConditions.isEmpty()) {
				return "";
			}

			StringBuffer sb = new StringBuffer();
			sb.append("WHERE");
			sb.append(SPACE);
			for (int i = 0; i < validConditions.size(); i++) {
				sb.append(validConditions.get(i));
				if (i < validConditions.size() - 1) {
					sb.append(SPACE);
					sb.append("OR");
					sb.append(SPACE);
				}
			}
			return sb.toString();
		}
	}

	/**
	 * <dl>
	 * <dt><b>クラス概要： </b></dt>
	 * <dd>WhereAnd/WhereOr要素に指定するための条件クラスです。 <br>
	 * 変数要素を0個以上含みます。 <br>
	 * パラメーター値がnullであり、 かつ、"= 変数要素"という書式になっている場合は、 "IS NULL"に変換します。 <br>
	 * パラメーター値がnullであり、 かつ、" <>変数要素"または"!= 変数要素"または"^= 変数要素" という書式になっている場合は、"IS
	 * NOT NULL"に変換します。</dd>
	 * </dl>
	 */
	private static class Condition implements I_Element {

		/**
		 * "="演算子です。
		 */
		private static final String EQUAL = "=";

		/**
		 * " <>"演算子です。
		 */
		private static final String NOT_EQUAL1 = "<>";

		/**
		 * "!="演算子です。
		 */
		private static final String NOT_EQUAL2 = "!=";

		/**
		 * "^="演算子です。
		 */
		private static final String NOT_EQUAL3 = "^=";

		/**
		 * 変数要素よりも前の文字列です。
		 */
		private String _preConstants = null;

		/**
		 * 変数要素です。
		 */
		private Variable _variable = null;

		/**
		 * 変数要素よりも後の文字列です。
		 */
		private String _postConstants = null;

		/**
		 * 変数が"IS NULL"の置換候補であるかどうかのフラグです。
		 */
		private boolean _isNullable = false;

		/**
		 * 変数が"IS NOT NULL"の置換候補であるかどうかのフラグです。
		 */
		private boolean _isNotNullable = false;

		/**
		 * <dl>
		 * <dt><b>コンストラクター概要： </b></dt>
		 * <dd>引数のリストを解析し、条件に含まれる変数の抽出と その変数が"IS NULL"/"IS NOT
		 * NULL"の置換候補であるかどうかを判定します。</dd>
		 * </dl>
		 * 
		 * @param sqlElements
		 *            条件に含まれる構文要素のリスト
		 * @exception IllegalArgumentException
		 *                一つの条件に複数の変数が含まれている場合
		 */
		private Condition(List sqlElements) throws IllegalArgumentException {
			//SQL要素インデックス
			int i = 0;

			//変数前の文字列を作成
			StringBuffer pre = new StringBuffer();
			while (i < sqlElements.size()) {
				I_Element e1 = (I_Element) sqlElements.get(i);
				if (e1 instanceof Variable) {
					_variable = (Variable) e1;
					i++;
					break;
				}
				if (e1 instanceof Constant) {
					String value = e1.format(null, null);
					//最終要素である場合は次の要素を判定しない
					if (i == sqlElements.size() - 1) {
						pre.append(value);
						i++;
						break;
					}
					//要素が"="で、次の要素がVariableであった場合はIsNullable
					if (EQUAL.equals(value)) {
						I_Element e2 = (I_Element) sqlElements.get(i + 1);
						if (e2 instanceof Variable) {
							_isNullable = true;
							i++;
							continue;
						}
					}
					//要素が"<>","!=","^="で、次の要素がVariableであった場合はIsNotNullable
					if (NOT_EQUAL1.equals(value) || NOT_EQUAL2.equals(value)
							|| NOT_EQUAL3.equals(value)) {
						I_Element e2 = (I_Element) sqlElements.get(i + 1);
						if (e2 instanceof Variable) {
							_isNotNullable = true;
							i++;
							continue;
						}
					}
					pre.append(value);
					pre.append(SPACE);
				}
				i++;
			}
			_preConstants = pre.toString();

			//変数後の文字列を作成
			StringBuffer post = new StringBuffer();
			while (i < sqlElements.size()) {
				I_Element e1 = (I_Element) sqlElements.get(i);
				if (e1 instanceof Variable) {
					//条件内に複数の変数を含めることはできない
					throw new IllegalArgumentException(MSG_PLURAL_VARIABLE);
				}
				if (e1 instanceof Constant) {
					post.append(e1.format(null, null));
				}
				if (i != sqlElements.size() - 1) {
					post.append(SPACE);
				}
				i++;
			}
			_postConstants = post.toString();
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>変数名を元にパラメーターから値を取得し、 変数をその値で置換した文字列を返します。 <br>
		 * パラメーターが存在しない場合は""(空文字列)を返します。 <br>
		 * 条件に変数が含まれ、かつ、 パラメーターが指定されていない場合は""(空文字列)を返します。 <br>
		 * <br>
		 * 条件に"IS NULL"置換候補の変数が含まれ、 かつ、値がnullである場合は、変数を"IS NULL"に置換した文字列を返します。
		 * <br>
		 * 条件に"IS NOT NULL"置換候補の変数が含まれ、 かつ、値がnullである場合は、変数を"IS NOT
		 * NULL"に置換した文字列を返します。 <br>
		 * それ以外の場合で、値がnullである場合は、変数を"NULL"に置換した文字列を返します。</dd>
		 * </dl>
		 * 
		 * @param param
		 *            パラメーター
		 * @param paramNames
		 *            パラメーター名のリスト。このメソッドでは使用しません。
		 * @return 条件文字列
		 * @exception IllegalArgumentException
		 *                パラメーターが不足していた場合
		 */
		public String format(Parameter param, List paramNames)
				throws IllegalArgumentException {

			StringBuffer sb = new StringBuffer();
			sb.append(_preConstants);

			if (_variable == null) {
				// 条件に変数が含まれていない場合
				sb.append(_postConstants);
				return sb.toString();
			}

			if ((param == null)
					|| ((_variable != null) && (!param.containsKey(_variable
							.getName())))) {
				// パラメーターが存在しない場合
				// または、条件に変数が含まれ、かつ、パラメーターが指定されていない場合
				return "";
			}

			Object value = param.getObject(_variable.getName());
			if (value == null) {
				if (_isNullable) {
					sb.append("IS NULL");
				} else if (_isNotNullable) {
					sb.append("IS NOT NULL");
				} else {
					sb.append(_variable.format(param, paramNames));
				}
			} else if (value != null) {
				if (_isNullable) {
					sb.append(EQUAL);
					sb.append(SPACE);
				} else if (_isNotNullable) {
					sb.append(NOT_EQUAL1);
					sb.append(SPACE);
				}
				sb.append(_variable.format(param, paramNames));
			}
			if (!_postConstants.equals("")) {
				sb.append(SPACE);
				sb.append(_postConstants);
			}
			return sb.toString();
		}
	}

	/**
	 * <dl>
	 * <dt><b>クラス概要： </b></dt>
	 * <dd>構文規則に基づいてSQLテンプレート文字列を解析し、 SQLテンプレートの構文要素リストを生成するクラスです。</dd>
	 * </dl>
	 * 
	 * @see PreparedStatementBuilder
	 */
	private class Parser {

		/**
		 * WhereAnd要素の接頭辞です。
		 */
		private static final String WHERE_AND = ":_WHERE_AND(";

		/**
		 * WhereOr要素の接頭辞です。
		 */
		private static final String WHERE_OR = ":_WHERE_OR(";

		/**
		 * コロンです。
		 */
		private static final char CHAR_COLON = ':';

		/**
		 * シングル・クォーテーションです。
		 */
		private static final char CHAR_SINGLEQUOTE = '\'';

		/**
		 * ダブル・クオーテーションです。
		 */
		private static final char CHAR_DOUBLEQUOTE = '"';

		/**
		 * 空白文字です。
		 */
		private static final char CHAR_SPACE = ' ';

		/**
		 * a(小文字)です。
		 */
		private static final char CHAR_LA = 'a';

		/**
		 * A(大文字)です。
		 */
		private static final char CHAR_UA = 'A';

		/**
		 * z(小文字)です。
		 */
		private static final char CHAR_LZ = 'z';

		/**
		 * Z(大文字)です。
		 */
		private static final char CHAR_UZ = 'Z';

		/**
		 * 0です。
		 */
		private static final char CHAR_ZERO = '0';

		/**
		 * 9です。
		 */
		private static final char CHAR_NINE = '9';

		/**
		 * アンダースコアです。
		 */
		private static final char CHAR_UNDERSCORE = '_';

		/**
		 * イコール演算子です。
		 */
		private static final char CHAR_EQUAL = '=';

		/**
		 * 小なり演算子です。
		 */
		private static final char CHAR_LT = '<';

		/**
		 * 大なり演算子です。
		 */
		private static final char CHAR_GT = '>';

		/**
		 * NOT演算子です。
		 */
		private static final char CHAR_CARET = '^';

		/**
		 * NOT演算子です。
		 */
		private static final char CHAR_EXCLAMATION = '!';

		/**
		 * カンマです。
		 */
		private static final char CHAR_COMMA = ',';

		/**
		 * 右括弧です。
		 */
		private static final char CHAR_RP = ')';

		/**
		 * 比較演算子です。
		 */
		private final char[] _comparisonOperators = { CHAR_EQUAL, CHAR_LT,
				CHAR_GT, CHAR_CARET, CHAR_EXCLAMATION };

		/**
		 * SQLテンプレート文字列長です。
		 */
		private int _length = _sqlTemplate.length();

		/**
		 * 文字の位置インデックスです。
		 */
		private int _i = 0;

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>解析処理を実行し、 SQLテンプレートの構文要素リストを生成します。</dd>
		 * </dl>
		 * 
		 * @return SQLテンプレートの構文要素リスト
		 */
		private List parse() {
			//SQL要素リスト
			List sqlElements = new ArrayList();

			while (_i < _length) {
				//単語の開始文字
				char c1 = _sqlTemplate.charAt(_i);

				if (c1 == CHAR_COLON) {
					//':'から始まる単語は変数として見なす

					//WHERE_AND構文判定
					if (_sqlTemplate.regionMatches(_i, WHERE_AND, 0, WHERE_AND
							.length())) {
						//WHERE_AND構文解析処理
						_i += WHERE_AND.length();
						WhereAnd whereAnd = new WhereAnd(parseConditions());
						sqlElements.add(whereAnd);
						continue;
					}

					//WHERE_OR構文判定
					if (_sqlTemplate.regionMatches(_i, WHERE_OR, 0, WHERE_OR
							.length())) {
						//WHERE_OR構文解析処理
						_i += WHERE_OR.length();
						WhereOr whereOr = new WhereOr(parseConditions());
						sqlElements.add(whereOr);
						continue;
					}

					//通常変数として解析
					addVariable(sqlElements);
					continue;
				} else if (isSingleQuotedLiteral(c1)) {
					//'\''(シングル・クォーテーション)から始まる単語は文字列として見なす
					addSingleQuotedLiteral(sqlElements);
					continue;
				} else if (c1 == CHAR_DOUBLEQUOTE) {
					//'"'(ダブル・クォーテーション)から始まる単語は文字列として見なす
					addDoubleQuoteLiteral(sqlElements);
					continue;
				} else if (c1 == CHAR_SPACE) {
					//' '(空白)は読み飛ばす
					_i++;
					continue;
				}
				//文字が':', '\'', '"', ' 'に該当しない場合は、
				//次の区切り文字が来るまで一つの単語として追加し続けて良い
				char[] terminalChars = { CHAR_COLON, CHAR_SINGLEQUOTE,
						CHAR_DOUBLEQUOTE, CHAR_SPACE };
				addOtherWord(sqlElements, terminalChars);
				continue;
			}
			return sqlElements;
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>WhereAnd/WhereOr要素内の条件を解析し、 条件のリストを生成します。</dd>
		 * </dl>
		 * 
		 * @return 条件リスト
		 * @exception IllegalArgumentException
		 *                終端記号')'を検出せずに文字列の最後まで達した場合
		 */
		private List parseConditions() throws IllegalArgumentException {
			//条件リスト
			List conditions = new ArrayList();
			//SQL要素リスト
			List sqlElements = new ArrayList();
			while (_i < _length) {
				//単語の開始文字
				char c1 = _sqlTemplate.charAt(_i);

				if (c1 == CHAR_COLON) {
					//':'から始まる単語は変数として見なす
					addVariable(sqlElements);
					continue;
				} else if ((c1 == CHAR_EQUAL) || (c1 == CHAR_LT)
						|| (c1 == CHAR_GT) || (c1 == CHAR_EXCLAMATION)
						|| (c1 == CHAR_CARET)) {
					//'=','<','>','!','^'から始まる単語は比較演算子と見なす
					addComparisonOperator(sqlElements);
					continue;
				} else if (isSingleQuotedLiteral(c1)) {
					//'\''(シングル・クォーテーション)から始まる単語は文字列として見なす
					addSingleQuotedLiteral(sqlElements);
					continue;
				} else if (c1 == CHAR_DOUBLEQUOTE) {
					//'"'(ダブル・クォーテーション)から始まる単語は文字列として見なす
					addDoubleQuoteLiteral(sqlElements);
					continue;
				} else if (c1 == CHAR_SPACE) {
					//' '(空白)は読み飛ばす
					_i++;
					continue;
				} else if (c1 == CHAR_COMMA) {
					//','から始まる単語は次の条件の開始と見なす
					_i++;
					//条件リストに現在の条件を追加
					Condition condition = new Condition(sqlElements);
					conditions.add(condition);

					//条件を初期化
					sqlElements = new ArrayList();
					continue;
				} else if (c1 == CHAR_RP) {
					//')'はWHERE_ANDの終端記号として見なす
					_i++;
					//条件リストに現在の条件を追加
					Condition condition = new Condition(sqlElements);
					conditions.add(condition);
					return conditions;
				}
				//文字が'\'', '"', ':', ' ', ',', ')', '=', '<', '>', '!',
				// '^'に該当しない場合は、
				//次の区切り文字が来るまで一つの単語として追加し続けて良い
				char[] delimiters = { CHAR_COLON, CHAR_EQUAL, CHAR_LT, CHAR_GT,
						CHAR_EXCLAMATION, CHAR_CARET, CHAR_SINGLEQUOTE,
						CHAR_DOUBLEQUOTE, CHAR_SPACE, CHAR_COMMA, CHAR_RP };
				addOtherWord(sqlElements, delimiters);
				continue;
			}
			//終端記号')'を検出せずに文字列の最後まで達した場合
			throw new IllegalArgumentException(MSG_UNCLOSED_PARENTHESIS);
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>変数文字列を解析し、 構文要素リストに変数要素インスタンスを追加します。</dd>
		 * </dl>
		 * 
		 * @param sqlElements
		 *            構文要素リスト
		 */
		private void addVariable(List sqlElements) {
			//単語
			StringBuffer word = new StringBuffer();
			//通常変数として解析
			_i++;
			//次の半角英数字と"_"以外の文字が来るまで一つの単語として追加し続けて良い
			while (_i < _length) {
				char c1 = _sqlTemplate.charAt(_i);
				if ((c1 < CHAR_LA || c1 > CHAR_LZ)
						&& (c1 < CHAR_UA || c1 > CHAR_UZ)
						&& (c1 < CHAR_ZERO || c1 > CHAR_NINE)
						&& (c1 != CHAR_UNDERSCORE)) {
					break;
				}
				word.append(c1);
				_i++;
			}
			sqlElements.add(new Variable(word.toString()));
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>"'"(シングル・クォーテーション)で囲まれた範囲を解析し、 構文要素リストに固定文字列要素インスタンスを追加します。
		 * </dd>
		 * </dl>
		 * 
		 * @param sqlElements
		 *            構文要素リスト
		 */
		private void addSingleQuotedLiteral(List sqlElements) {
			//単語
			StringBuffer word = new StringBuffer();

			//単語の開始文字
			char c1 = _sqlTemplate.charAt(_i);
			word.append(c1);
			_i++;
			if (((c1 == 'G') || (c1 == 'g')) && (_i < _length)
					&& (_sqlTemplate.charAt(_i) == CHAR_SINGLEQUOTE)) {
				word.append(CHAR_SINGLEQUOTE);
				_i++;
			}

			//次の'\''が来るまで文字を追加し続けて良い
			//ただし、'\''が２つ並んでいる場合は文字としての'\''と見なす
			while (_i < _length) {
				char c2 = _sqlTemplate.charAt(_i);
				word.append(c2);
				_i++;

				if (c2 != CHAR_SINGLEQUOTE) {
					//'\''ではない場合は追加し続ける
					continue;
				}

				if (_i >= _length) {
					break;
				}

				//'\''に遭遇した場合は２つ並んでいるかどうかを確認する
				char c3 = _sqlTemplate.charAt(_i);
				if (c3 != CHAR_SINGLEQUOTE) {
					//２つ並んでいない場合は区切り文字として見なしループを抜ける
					break;
				}
				//次の文字も'\''の場合は文字として見なす
				word.append(c3);
				_i++;
			}
			sqlElements.add(new Constant(word.toString()));
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>"""(ダブル・クォーテーション)で囲まれた範囲を解析し、 構文要素リストに固定文字列要素インスタンスを追加します。</dd>
		 * </dl>
		 * 
		 * @param sqlElements
		 *            構文要素リスト
		 */
		private void addDoubleQuoteLiteral(List sqlElements) {
			//単語
			StringBuffer word = new StringBuffer();
			//単語の開始文字
			char c1 = _sqlTemplate.charAt(_i);

			//'"'(ダブル・クォーテーション)から始まる単語は文字列として見なす
			word.append(c1);
			_i++;
			//次の'"'が来るまで文字を追加し続けて良い
			while (_i < _length) {
				char c2 = _sqlTemplate.charAt(_i);
				word.append(c2);
				_i++;
				if (c2 == CHAR_DOUBLEQUOTE) {
					break;
				}
			}
			sqlElements.add(new Constant(word.toString()));
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>比較演算子を表す文字列を解析し、 構文要素リストに固定文字列要素インスタンスを追加します。</dd>
		 * </dl>
		 * 
		 * @param sqlElements
		 *            構文要素リスト
		 */
		private void addComparisonOperator(List sqlElements) {
			//単語
			StringBuffer word = new StringBuffer();
			//単語の開始文字
			char c1 = _sqlTemplate.charAt(_i);

			word.append(c1);
			_i++;
			//比較演算子以外の文字が来るまで文字を追加しつづけてよい。
			while (_i < _length) {
				char c2 = _sqlTemplate.charAt(_i);
				//比較演算子かどうかを判定
				boolean match = false;
				for (int j = 0; j < _comparisonOperators.length; j++) {
					if (c2 == _comparisonOperators[j]) {
						match = true;
						break;
					}
				}
				if (!match) {
					break;
				}
				word.append(c2);
				_i++;
			}
			sqlElements.add(new Constant(word.toString()));
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>その他の文字列を解析し、 構文要素リストに固定文字列要素インスタンスを追加します。</dd>
		 * </dl>
		 * 
		 * @param sqlElements
		 *            構文要素リスト
		 * @param delimiters
		 *            区切り文字の配列
		 */
		private void addOtherWord(List sqlElements, char[] delimiters) {
			//単語
			StringBuffer word = new StringBuffer();
			//単語の開始文字
			char c1 = _sqlTemplate.charAt(_i);

			word.append(c1);
			_i++;
			//次の区切り文字が来るまで文字を追加しつづけてよい。
			while (_i < _length) {
				char c2 = _sqlTemplate.charAt(_i);
				boolean match = false;
				for (int j = 0; j < delimiters.length; j++) {
					if (c2 == delimiters[j]) {
						match = true;
						break;
					}
				}
				if (match) {
					break;
				}
				word.append(c2);
				_i++;
			}
			sqlElements.add(new Constant(word.toString()));
		}

		/**
		 * <dl>
		 * <dt><b>メソッド概要： </b></dt>
		 * <dd>"'"(シングル・クォーテーション)で囲まれた文字列かどうかを判定します。
		 * GRAPHIC型の書式(例：G'ABC')もこれに該当します。</dd>
		 * </dl>
		 * 
		 * @param c1
		 *            文字
		 * @return 判定結果
		 */
		private boolean isSingleQuotedLiteral(char c1) {
			if (c1 == CHAR_SINGLEQUOTE) {
				return true;
			}
			return ((c1 == 'G') || (c1 == 'g')) && (_i + 1 < _length)
					&& (_sqlTemplate.charAt(_i + 1) == CHAR_SINGLEQUOTE);
		}
	}
}